package drds.binlog;


import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.BitSet;


public class LogByteBlock
{

    /* The max ulonglong - 0x ff ff ff ff ff ff ff ff */
    public static final BigInteger BIGINT_MAX_VALUE = new BigInteger("18446744073709551615");
    public static final long NULL_LENGTH = ((long) ~0);
    /* default ANSI charset */
    public static final String ISO_8859_1 = "ISO-8859-1";
    /* decimal representation */
    public static final int DIG_PER_DEC1 = 9;
    public static final int DIG_BASE = 1000000000;
    public static final int DIG_MAX = DIG_BASE - 1;
    public static final int dig2bytes[] = {0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
    public static final int powersBy10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
    public static final int DIG_PER_INT32 = 9;
    public static final int SIZE_OF_INT32 = 4;
    //
    protected byte[] bytes;
    protected int originIndex;//所有的偏移量都以这个为准
    protected int limit;
    //
    protected int index;
    //
    protected int semival;

    protected LogByteBlock()
    {
    }

    //init bytes
    public LogByteBlock(byte[] bytes, final int originIndex, final int limit)
    {
        if (originIndex + limit > bytes.length)
        {
            throw new IllegalArgumentException("capacity excceed: " + (originIndex + limit));
        }


        this.bytes = bytes;
        this.originIndex = originIndex;
        this.limit = limit;
        //
        this.index = originIndex;

    }

    /**
     * Return 16-bit signed int from bytes. (big-endian)
     *
     * @see mysql-5.1.60/include/myisampack.h - mi_sint2korr
     */
    private static final int get2IntBigEndian(byte[] bytes, final int index)
    {
        return ((bytes[index]) << 8) | (0xff & bytes[index + 1]);
    }

    /**
     * Return 24-bit signed int from bytes. (big-endian)
     *
     * @see mysql-5.1.60/include/myisampack.h - mi_sint3korr
     */
    private static final int get3IntBigEndian(byte[] bytes, final int index)
    {
        return (bytes[index] << 16) | ((0xff & bytes[index + 1]) << 8) | (0xff & bytes[index + 2]);
    }

    /**
     * Return 32-bit signed int from bytes. (big-endian)
     *
     * @see mysql-5.1.60/include/myisampack.h - mi_sint4korr
     */
    private static final int get4IntBigEndian(byte[] bytes, final int index)
    {
        return (bytes[index] << 24) | ((0xff & bytes[index + 1]) << 16) | ((0xff & bytes[index + 2]) << 8) | (0xff & bytes[index + 3]);
    }


    /**
     * @param offset base on originIndex
     */
    public final LogByteBlock duplicate()
    {
        byte[] bytes = Arrays.copyOfRange(this.bytes, originIndex, originIndex + limit);
        return new LogByteBlock(bytes, 0, limit);
    }

    /**
     * @param offset base on originIndex
     */
    public final LogByteBlock duplicate(final int offset, final int length)
    {
        if (offset + length > limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (offset + length));
        }

        final int index = originIndex + offset;
        byte[] bytes = Arrays.copyOfRange(this.bytes, index, index + length);
        return new LogByteBlock(bytes, 0, length);
    }

    /**
     * @param length base on index
     */
    public final LogByteBlock duplicateBasedOnIndex(final int length)
    {
        if (index + length > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index + length - originIndex));
        }


        final int endIndex = index + length;
        byte[] bytes = Arrays.copyOfRange(this.bytes, index, endIndex);
        LogByteBlock logByteBlock = new LogByteBlock(bytes, 0, length);
        index = endIndex;
        return logByteBlock;
    }
    //

    /**
     * Returns this bytes's capacity. </p>
     *
     * @return The capacity of this bytes
     */
    public final int capacity()
    {
        return bytes.length;
    }

    /**
     * Returns this bytes's index$originIndex. </p>
     *
     * @return The index$originIndex of this bytes
     */
    public final int index$originIndex()
    {
        return index - originIndex;
    }

    /**
     * Sets this bytes's index$originIndex. If the mark is defined and larger than the
     * new index$originIndex then it is discarded. </p>
     *
     * @param offset The new index$originIndex value; must be non-negative and no
     *               larger than the current limit
     * @return This bytes
     * @throws IllegalArgumentException If the preconditions on
     *                                  <tt>newPosition</tt> do not hold
     */
    public final LogByteBlock index(final int offset)
    {
        if (offset > limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + offset);
        }
        this.index = originIndex + offset;
        return this;
    }

    /**
     * Forwards this bytes's index$originIndex.
     *
     * @param offset The forwardBasedOnIndex distance
     * @return This bytes
     */
    public final LogByteBlock forwardBasedOnIndex(final int offset)
    {
        if (index + offset > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index + offset - originIndex));
        }

        this.index += offset;
        return this;
    }

    /**
     * Consume this bytes, moving originIndex and index$originIndex.
     *
     * @param length The consume distance
     * @return This bytes
     */
    public final LogByteBlock consume(final int length)
    {
        if (limit > length)
        {
            limit -= length;
            originIndex += length;
            index = originIndex;
            return this;
        } else if (limit == length)
        {
            limit = 0;
            originIndex = 0;
            index = 0;
            return this;
        } else
        {
            /* Should not happen. */
            throw new IllegalArgumentException("limit excceed: " + length);
        }
    }

    /**
     * Rewinds this bytes. The index$originIndex is set to zero.
     *
     * @return This bytes
     */
    public final LogByteBlock rewind()
    {
        index = originIndex;
        return this;
    }

    /**
     * Returns this bytes's limit. </p>
     *
     * @return The limit of this bytes
     */
    public final int limit()
    {
        return limit;
    }

    /**
     * Sets this bytes's limit. If the index$originIndex is larger than the new limit
     * then it is set to the new limit. If the mark is defined and larger than
     * the new limit then it is discarded. </p>
     *
     * @param newLimit The new limit value; must be non-negative and no larger
     *                 than this bytes's capacity
     * @return This bytes
     * @throws IllegalArgumentException If the preconditions on
     *                                  <tt>newLimit</tt> do not hold
     */
    public final LogByteBlock limit(int newLimit)
    {
        if (originIndex + newLimit > bytes.length || newLimit < 0)
        {
            throw new IllegalArgumentException("capacity excceed: " + (originIndex + newLimit));
        }


        limit = newLimit;
        return this;
    }

    /**
     * Returns the number of elements between the current index$originIndex and the
     * limit. </p>
     *
     * @return The number of elements remaining in this bytes
     */
    public final int remaining()
    {
        return originIndex + limit - index;
    }

    /**
     * Tells whether there are any elements between the current index$originIndex and the
     * limit. </p>
     *
     * @return <tt>true</tt> if, and only if, there is at least one element
     * remaining in this bytes
     */
    public final boolean hasRemaining()
    {
        return index < originIndex - limit;
    }

    /**
     * Return 8-bit signed int from bytes.
     */
    public final int getInt8(final int offset)
    {
        if (offset >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + offset);
        }

        return bytes[originIndex + offset];
    }

    /**
     * Return next 8-bit signed int from bytes.
     */
    public final int getInt8BasedOnIndex()
    {
        if (index >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex));
        }
        return bytes[index++];
    }

    /**
     * Return 8-bit unsigned int from bytes.
     */
    public final int getUint8(final int offset)
    {
        if (offset >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + offset);
        }

        return 0xff & bytes[originIndex + offset];
    }

    /**
     * Return next 8-bit unsigned int from bytes.
     */
    public final int getUint8BasedOnIndex()
    {
        if (index >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex));
        }

        return 0xff & bytes[index++];
    }


    /**
     * Return next 16-bit signed int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint2korr
     */
    public final int getInt16BasedOnIndex()
    {
        if (index + 1 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 1));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index++]) | ((bytes[index++]) << 8);
    }

    /**
     * Return 16-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint2korr
     */
    public final int getUint16(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 1 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + (offset < 0 ? offset : (offset + 1)));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index]) | ((0xff & bytes[index + 1]) << 8);
    }

    /**
     * Return next 16-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint2korr
     */
    public final int getUint16BasedOnIndex()
    {
        if (index + 1 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 1));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index++]) | ((0xff & bytes[index++]) << 8);
    }


    /**
     * Return next 16-bit signed int from bytes. (big-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - mi_sint2korr
     */
    public final int getBeInt16BasedOnIndex()
    {
        if (index + 1 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 1));
        }

        byte[] bytes = this.bytes;
        return (bytes[index++] << 8) | (0xff & bytes[index++]);
    }


    /**
     * Return next 16-bit unsigned int from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_usint2korr
     */
    public final int getBeUint16BasedOnIndex()
    {
        if (index + 1 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 1));
        }

        byte[] bytes = this.bytes;
        return ((0xff & bytes[index++]) << 8) | (0xff & bytes[index++]);
    }

    /**
     * Return 24-bit signed int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint3korr
     */
    public final int getInt24(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 2 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + (offset < 0 ? offset : (offset + 2)));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index]) | ((0xff & bytes[index + 1]) << 8) | ((bytes[index + 2]) << 16);
    }

    /**
     * Return next 24-bit signed int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint3korr
     */
    public final int getInt24BasedOnIndex()
    {
        if (index + 2 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 2));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index++]) | ((0xff & bytes[index++]) << 8) | ((bytes[index++]) << 16);
    }


    /**
     * Return next 24-bit signed int from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_usint3korr
     */
    public final int getBeInt24BasedOnIndex()
    {
        if (index + 2 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index - originIndex + 2));
        }

        byte[] bytes = this.bytes;
        return ((bytes[index++]) << 16) | ((0xff & bytes[index++]) << 8) | (0xff & bytes[index++]);
    }

    /**
     * Return 24-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint3korr
     */
    public final int getUint24(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 2 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (offset < 0 ? offset : (offset + 2)));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index]) | ((0xff & bytes[index + 1]) << 8) | ((0xff & bytes[index + 2]) << 16);
    }

    /**
     * Return next 24-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint3korr
     */
    public final int getUint24BasedOnIndex()
    {
        if (index + 2 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 2));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index++]) | ((0xff & bytes[index++]) << 8) | ((0xff & bytes[index++]) << 16);
    }


    /**
     * Return next 24-bit unsigned int from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_usint3korr
     */
    public final int getBeUint24BasedOnIndex()
    {
        if (index + 2 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 2));
        }

        byte[] bytes = this.bytes;
        return ((0xff & bytes[index++]) << 16) | ((0xff & bytes[index++]) << 8) | (0xff & bytes[index++]);
    }

    /**
     * Return 32-bit signed int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint4korr
     */
    public final int getInt32(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 3 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (offset < 0 ? offset : (offset + 3)));
        }

        byte[] bytes = this.bytes;
        return (0xff & bytes[index]) | ((0xff & bytes[index + 1]) << 8) | ((0xff & bytes[index + 2]) << 16)
                | ((bytes[index + 3]) << 24);
    }


    /**
     * Return next 32-bit signed int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint4korr
     */
    public final int getInt32BasedOnIndex()
    {
        if (index + 3 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 3));
        }
        byte[] bytes = this.bytes;
        return (0xff & bytes[index++]) | ((0xff & bytes[index++]) << 8) | ((0xff & bytes[index++]) << 16)
                | ((bytes[index++]) << 24);
    }


    /**
     * Return 32-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint4korr
     */
    public final long getUint32(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 3 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (offset < 0 ? offset : (offset + 3)));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index])) | ((long) (0xff & bytes[index + 1]) << 8)
                | ((long) (0xff & bytes[index + 2]) << 16) | ((long) (0xff & bytes[index + 3]) << 24);
    }


    /**
     * Return next 32-bit unsigned int from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint4korr
     */
    public final long getUint32BasedOnIndex()
    {
        if (index + 3 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 3));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++])) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 24);
    }

    /**
     * Return next 32-bit unsigned int from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_uint4korr
     */
    public final long getBeUint32BasedOnIndex()
    {
        if (index + 3 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 3));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++]) << 24) | ((long) (0xff & bytes[index++]) << 16)
                | ((long) (0xff & bytes[index++]) << 8) | ((long) (0xff & bytes[index++]));
    }


    /**
     * Return next 40-bit unsigned int from bytes. (little-endian)
     */
    public final long getUlong40BasedOnIndex()
    {
        if (index + 4 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 4));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++])) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 32);
    }


    /**
     * Return next 40-bit unsigned int from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_uint5korr
     */
    public final long getBeUlong40BasedOnIndex()
    {
        if (index + 4 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 4));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++]) << 32) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]));
    }


    /**
     * Return next 48-bit unsigned long from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint6korr
     */
    public final long getUlong48BasedOnIndex()
    {
        if (index + 5 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 5));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++])) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 32) | ((long) (0xff & bytes[index++]) << 40);
    }

    /**
     * Return next 48-bit unsigned long from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_uint6korr
     */
    public final long getBeUlong48BasedOnIndex()
    {
        if (index + 5 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 5));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++]) << 40) | ((long) (0xff & bytes[index++]) << 32)
                | ((long) (0xff & bytes[index++]) << 24) | ((long) (0xff & bytes[index++]) << 16)
                | ((long) (0xff & bytes[index++]) << 8) | ((long) (0xff & bytes[index++]));
    }


    /**
     * Return next 56-bit unsigned int from bytes. (little-endian)
     */
    public final long getUlong56BasedOnIndex()
    {
        if (index + 6 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 6));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++])) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 32) | ((long) (0xff & bytes[index++]) << 40)
                | ((long) (0xff & bytes[index++]) << 48);
    }


    /**
     * Return next 56-bit unsigned int from bytes. (big-endian)
     */
    public final long getBeUlong56BasedOnIndex()
    {
        if (index + 6 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 6));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++]) << 48) | ((long) (0xff & bytes[index++]) << 40)
                | ((long) (0xff & bytes[index++]) << 32) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]));
    }

    /**
     * Return 64-bit signed long from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint8korr
     */
    public final long getLong64(final int offset)
    {
        final int index = originIndex + offset;

        if (offset + 7 >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (offset < 0 ? offset : (offset + 7)));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index])) | ((long) (0xff & bytes[index + 1]) << 8)
                | ((long) (0xff & bytes[index + 2]) << 16) | ((long) (0xff & bytes[index + 3]) << 24)
                | ((long) (0xff & bytes[index + 4]) << 32) | ((long) (0xff & bytes[index + 5]) << 40)
                | ((long) (0xff & bytes[index + 6]) << 48) | ((long) (bytes[index + 7]) << 56);
    }


    /**
     * Return next 64-bit signed long from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - sint8korr
     */
    public final long getLong64BasedOnIndex()
    {
        if (index + 7 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 7));
        }

        byte[] bytes = this.bytes;
        return ((long) (0xff & bytes[index++])) | ((long) (0xff & bytes[index++]) << 8)
                | ((long) (0xff & bytes[index++]) << 16) | ((long) (0xff & bytes[index++]) << 24)
                | ((long) (0xff & bytes[index++]) << 32) | ((long) (0xff & bytes[index++]) << 40)
                | ((long) (0xff & bytes[index++]) << 48) | ((long) (bytes[index++]) << 56);
    }

    /**
     * Return next 64-bit signed long from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_sint8korr
     */
    public final long getBeLong64BasedOnIndex()
    {
        if (index + 7 >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index - originIndex + 7));
        }

        byte[] bytes = this.bytes;
        return ((long) (bytes[index++]) << 56) | ((long) (0xff & bytes[index++]) << 48)
                | ((long) (0xff & bytes[index++]) << 40) | ((long) (0xff & bytes[index++]) << 32)
                | ((long) (0xff & bytes[index++]) << 24) | ((long) (0xff & bytes[index++]) << 16)
                | ((long) (0xff & bytes[index++]) << 8) | ((long) (0xff & bytes[index++]));
    }

    /**
     * Return 64-bit unsigned long from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint8korr
     */
    public final BigInteger getUlong64(final int offset)
    {
        final long long64 = getLong64(offset);

        return (long64 >= 0) ? BigInteger.valueOf(long64) : BIGINT_MAX_VALUE.add(BigInteger.valueOf(1 + long64));
    }


    /**
     * Return next 64-bit unsigned long from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - uint8korr
     */
    public final BigInteger getUlong64()
    {
        final long long64 = getLong64BasedOnIndex();

        return (long64 >= 0) ? BigInteger.valueOf(long64) : BIGINT_MAX_VALUE.add(BigInteger.valueOf(1 + long64));
    }

    /**
     * Return next 64-bit unsigned long from bytes. (big-endian)
     *
     * @see mysql-5.6.10/include/myisampack.h - mi_uint8korr
     */
    public final BigInteger getBeUlong64()
    {
        final long long64 = getBeLong64BasedOnIndex();

        return (long64 >= 0) ? BigInteger.valueOf(long64) : BIGINT_MAX_VALUE.add(BigInteger.valueOf(1 + long64));
    }


    /**
     * Return next 32-bit float from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - float4get
     */
    public final float getFloat32()
    {
        return Float.intBitsToFloat(getInt32BasedOnIndex());
    }


    /**
     * Return next 64-bit double from bytes. (little-endian)
     *
     * @see mysql-5.1.60/include/my_global.h - float8get
     */
    public final double getDouble64()
    {
        return Double.longBitsToDouble(getLong64BasedOnIndex());
    }


    /**
     * Return next packed number from bytes. (little-endian)
     *
     * @see LogByteBlock#getPackedLong(int)
     */
    public final long getPackedLong()
    {
        final int lead = getUint8BasedOnIndex();
        if (lead < 251) return lead;

        switch (lead)
        {
            case 251:
                return NULL_LENGTH;
            case 252:
                return getUint16BasedOnIndex();
            case 253:
                return getUint24BasedOnIndex();
            default: /* Must be 254 when here */
                final long value = getUint32BasedOnIndex();
                index += 4; /* ignore other */
                return value;
        }
    }


    /**
     * Return next fix length string from bytes.
     */
    public final String getFixString(final int position)
    {
        return getFixStringBasedOnIndex(position, ISO_8859_1);
    }


    /**
     * Return next fix length string from bytes.
     */
    public final String getFixStringBasedOnIndex(final int length, String charsetName)
    {
        if (index + length > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index + length - originIndex));
        }

        final int from = index;
        final int end = from + length;
        byte[] bytes = this.bytes;
        int found = from;
        for (; (found < end) && bytes[found] != '\0'; found++)
            /* empty loop */
            ;

        try
        {
            String string = new String(bytes, from, found - from, charsetName);
            index += length;
            return string;
        } catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }

    /**
     * Return fix-length string from bytes without null-terminate checking. Fix
     * bug #17 {@link https://github.com/AlibabaTech/canal/issues/17 }
     */
    public final String getFullString(final int offset, final int length, String charsetName)
    {
        if (offset + length > limit || offset < 0) throw new IllegalArgumentException("limit excceed: "
                + (offset < 0 ? offset : (offset + length)));

        try
        {
            return new String(bytes, originIndex + offset, length, charsetName);
        } catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }

    /**
     * Return next fix-length string from bytes without null-terminate
     * checking. Fix bug #17 {@link https
     * ://github.com/AlibabaTech/canal/issues/17 }
     */
    public final String getFullStringBasedOnIndex(final int length, String charsetName)
    {
        if (index + length > originIndex + limit) throw new IllegalArgumentException("limit excceed: "
                + (index + length - originIndex));

        try
        {
            String string = new String(bytes, index, length, charsetName);
            index += length;
            return string;
        } catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }

    /**
     * Return dynamic length string from bytes.
     */
    public final String getString(final int pos)
    {
        return getString(pos, ISO_8859_1);
    }

    /**
     * Return next dynamic length string from bytes.
     */
    public final String getString()
    {
        return getStringBasedOnIndex(ISO_8859_1);
    }

    /**
     * Return dynamic length string from bytes.
     */
    public final String getString(final int offset, String charsetName)
    {
        if (offset >= limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + offset);
        }

        byte[] bytes = this.bytes;
        final int length = (0xff & bytes[originIndex + offset]);
        if (offset + length + 1 > limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (offset + length + 1));
        }

        try
        {
            return new String(bytes, originIndex + offset + 1, length, charsetName);
        } catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }

    /**
     * Return next dynamic length string from bytes.
     */
    public final String getStringBasedOnIndex(String charsetName)
    {
        if (index >= originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + index);
        }

        byte[] bytes = this.bytes;
        final int length = (0xff & bytes[index]);
        if (index + length + 1 > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index + length + 1 - originIndex));
        }

        try
        {
            String string = new String(bytes, index + 1, length, charsetName);
            index += length + 1;
            return string;
        } catch (UnsupportedEncodingException e)
        {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }

    /**
     * Return big decimal from bytes.
     *
     * @see mysql-5.1.60/strings/decimal.c - bin2decimal()
     */
    public final BigDecimal getDecimal(final int offset, final int precision, final int scale)
    {
        final int intg = precision - scale;
        final int frac = scale;
        final int intg0 = intg / DIG_PER_INT32;
        final int frac0 = frac / DIG_PER_INT32;
        final int intg0x = intg - intg0 * DIG_PER_INT32;
        final int frac0x = frac - frac0 * DIG_PER_INT32;

        final int binSize = intg0 * SIZE_OF_INT32 + dig2bytes[intg0x] + frac0 * SIZE_OF_INT32 + dig2bytes[frac0x];
        if (offset + binSize > limit || offset < 0)
        {
            throw new IllegalArgumentException("limit excceed: " + (offset < 0 ? offset : (offset + binSize)));
        }
        return getDecimal0(originIndex + offset, intg, frac, // NL
                intg0,
                frac0,
                intg0x,
                frac0x);
    }
    ////////////////////////////////////////////////////////////////////////////////////

    /**
     * Return next big decimal from bytes.
     *
     * @see mysql-5.1.60/strings/decimal.c - bin2decimal()
     */
    public final BigDecimal getDecimal(final int precision, final int scale)
    {
        final int intg = precision - scale;
        final int frac = scale;
        final int intg0 = intg / DIG_PER_INT32;
        final int frac0 = frac / DIG_PER_INT32;
        final int intg0x = intg - intg0 * DIG_PER_INT32;
        final int frac0x = frac - frac0 * DIG_PER_INT32;

        final int binSize = intg0 * SIZE_OF_INT32 + dig2bytes[intg0x] + frac0 * SIZE_OF_INT32 + dig2bytes[frac0x];
        if (index + binSize > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: " + (index + binSize - originIndex));
        }

        BigDecimal decimal = getDecimal0(index, intg, frac, // NL
                intg0,
                frac0,
                intg0x,
                frac0x);
        index += binSize;
        return decimal;
    }

    /**
     * Return big decimal from bytes.
     * <p>
     * <pre>
     * Decimal representation in binlog seems to be as follows:
     *
     * 1st bit - sign such that set == +, unset == -
     * every 4 bytes represent 9 digits in big-endian order, so that
     * if you print the values of these quads as big-endian integers one after
     * another, you get the whole number string representation in decimal. What
     * remains is to put a sign and a decimal dot.
     *
     * 80 00 00 05 1b 38 b0 60 00 means:
     *
     *   0x80 - positive
     *   0x00000005 - 5
     *   0x1b38b060 - 456700000
     *   0x00       - 0
     *
     * 54567000000 / 10^{10} = 5.4567
     * </pre>
     *
     * @see mysql-5.1.60/strings/decimal.c - bin2decimal()
     * @see mysql-5.1.60/strings/decimal.c - decimal2string()
     */
    private final BigDecimal getDecimal0(final int begin, final int intg, final int frac, final int intg0,
                                         final int frac0, final int intg0x, final int frac0x)
    {
        final int mask = ((bytes[begin] & 0x80) == 0x80) ? 0 : -1;
        int from = begin;

        /* max string length */
        final int length = ((mask != 0) ? 1 : 0) + ((intg != 0) ? intg : 1) // NL
                + ((frac != 0) ? 1 : 0) + frac;
        char[] chars = new char[length];
        int pos = 0;

        if (mask != 0) /* decimal sign */
            chars[pos++] = ('-');

        final byte[] d_copy = bytes;
        d_copy[begin] ^= 0x80; /* clear sign */
        int mark = pos;

        if (intg0x != 0)
        {
            final int i = dig2bytes[intg0x];
            int x = 0;
            switch (i)
            {
                case 1:
                    x = d_copy[from] /* one byte */;
                    break;
                case 2:
                    x = get2IntBigEndian(d_copy, from);
                    break;
                case 3:
                    x = get3IntBigEndian(d_copy, from);
                    break;
                case 4:
                    x = get4IntBigEndian(d_copy, from);
                    break;
            }
            from += i;
            x ^= mask;
            if (x < 0 || x >= powersBy10[intg0x + 1])
            {
                throw new IllegalArgumentException("bad format, x exceed: " + x + ", " + powersBy10[intg0x + 1]);
            }
            if (x != 0 /* !digit || x != 0 */)
            {
                for (int j = intg0x; j > 0; j--)
                {
                    final int divisor = powersBy10[j - 1];
                    final int y = x / divisor;
                    if (mark < pos || y != 0)
                    {
                        chars[pos++] = ((char) ('0' + y));
                    }
                    x -= y * divisor;
                }
            }
        }

        for (final int stop = from + intg0 * SIZE_OF_INT32; from < stop; from += SIZE_OF_INT32)
        {
            int x = get4IntBigEndian(d_copy, from);
            x ^= mask;
            if (x < 0 || x > DIG_MAX)
            {
                throw new IllegalArgumentException("bad format, x exceed: " + x + ", " + DIG_MAX);
            }
            if (x != 0)
            {
                if (mark < pos)
                {
                    for (int i = DIG_PER_DEC1; i > 0; i--)
                    {
                        final int divisor = powersBy10[i - 1];
                        final int y = x / divisor;
                        chars[pos++] = ((char) ('0' + y));
                        x -= y * divisor;
                    }
                } else
                {
                    for (int i = DIG_PER_DEC1; i > 0; i--)
                    {
                        final int divisor = powersBy10[i - 1];
                        final int y = x / divisor;
                        if (mark < pos || y != 0)
                        {
                            chars[pos++] = ((char) ('0' + y));
                        }
                        x -= y * divisor;
                    }
                }
            } else if (mark < pos)
            {
                for (int i = DIG_PER_DEC1; i > 0; i--)
                    chars[pos++] = ('0');
            }
        }

        if (mark == pos)
        /* fix 0.0 problem, only '.' may cause BigDecimal parsing exception. */
            chars[pos++] = ('0');

        if (frac > 0)
        {
            chars[pos++] = ('.');
            mark = pos;

            for (final int stop = from + frac0 * SIZE_OF_INT32; from < stop; from += SIZE_OF_INT32)
            {
                int x = get4IntBigEndian(d_copy, from);
                x ^= mask;
                if (x < 0 || x > DIG_MAX)
                {
                    throw new IllegalArgumentException("bad format, x exceed: " + x + ", " + DIG_MAX);
                }
                if (x != 0)
                {
                    for (int i = DIG_PER_DEC1; i > 0; i--)
                    {
                        final int divisor = powersBy10[i - 1];
                        final int y = x / divisor;
                        chars[pos++] = ((char) ('0' + y));
                        x -= y * divisor;
                    }
                } else
                {
                    for (int i = DIG_PER_DEC1; i > 0; i--)
                        chars[pos++] = ('0');
                }
            }

            if (frac0x != 0)
            {
                final int i = dig2bytes[frac0x];
                int x = 0;
                switch (i)
                {
                    case 1:
                        x = d_copy[from] /* one byte */;
                        break;
                    case 2:
                        x = get2IntBigEndian(d_copy, from);
                        break;
                    case 3:
                        x = get3IntBigEndian(d_copy, from);
                        break;
                    case 4:
                        x = get4IntBigEndian(d_copy, from);
                        break;
                }
                x ^= mask;
                if (x != 0)
                {
                    final int dig = DIG_PER_DEC1 - frac0x;
                    x *= powersBy10[dig];
                    if (x < 0 || x > DIG_MAX)
                    {
                        throw new IllegalArgumentException("bad format, x exceed: " + x + ", " + DIG_MAX);
                    }
                    for (int j = DIG_PER_DEC1; j > dig; j--)
                    {
                        final int divisor = powersBy10[j - 1];
                        final int y = x / divisor;
                        chars[pos++] = ((char) ('0' + y));
                        x -= y * divisor;
                    }
                }
            }

            if (mark == pos)
            /* make number more friendly */
                chars[pos++] = ('0');
        }

        d_copy[begin] ^= 0x80; /* restore sign */
        String decimal = String.valueOf(chars, 0, pos);
        return new BigDecimal(decimal);
    }


    /**
     * Fill next MY_BITMAP structure from bytes.
     *
     * @param length The length of MY_BITMAP in bits.
     */
    public final void fillBitmap(BitSet bitmap, final int length)
    {
        if (index + ((length + 7) / 8) > originIndex + limit)
        {
            throw new IllegalArgumentException("limit excceed: "
                    + (index
                    + ((length + 7) / 8) - originIndex));
        }

        index = fillBitmap0(bitmap, index, length);
    }

    /**
     * Fill MY_BITMAP structure from bytes.
     *
     * @param length The length of MY_BITMAP in bits.
     */
    private final int fillBitmap0(BitSet bitmap, int index, final int length)
    {
        final byte[] bytes = this.bytes;

        for (int bit = 0; bit < length; bit += 8)
        {
            int flag = ((int) bytes[index++]) & 0xff;
            if (flag == 0) continue;
            if ((flag & 0x01) != 0) bitmap.set(bit);
            if ((flag & 0x02) != 0) bitmap.set(bit + 1);
            if ((flag & 0x04) != 0) bitmap.set(bit + 2);
            if ((flag & 0x08) != 0) bitmap.set(bit + 3);
            if ((flag & 0x10) != 0) bitmap.set(bit + 4);
            if ((flag & 0x20) != 0) bitmap.set(bit + 5);
            if ((flag & 0x40) != 0) bitmap.set(bit + 6);
            if ((flag & 0x80) != 0) bitmap.set(bit + 7);
        }
        return index;
    }


    /**
     * Return next MY_BITMAP structure from bytes.
     *
     * @param length The length of MY_BITMAP in bits.
     */
    public final BitSet getBitmap(final int length)
    {
        BitSet bitmap = new BitSet(length);
        fillBitmap(bitmap, length);
        return bitmap;
    }


    /**
     * Fill n bytes in this bytes.
     */
    public final void fillBytes(final int offset, byte[] bytes, final int startIndex, final int length)
    {
        if (offset + length > limit || offset < 0)
            throw new IllegalArgumentException("limit excceed: " + (offset + length));

        System.arraycopy(this.bytes, originIndex + offset, bytes, startIndex, length);
    }

    /**
     * Fill next n bytes in this bytes.
     */
    public final void fillBytes(byte[] bytes, final int startIndex, final int length)
    {
        if (index + length > originIndex + limit) throw new IllegalArgumentException("limit excceed: "
                + (index + length - originIndex));

        System.arraycopy(this.bytes, index, bytes, startIndex, length);
        index += length;
    }

    /**
     * Return n-byte data from bytes.
     */
    public final byte[] getData(final int pos, final int length)
    {
        byte[] bytes = new byte[length];
        fillBytes(pos, bytes, 0, length);
        return bytes;
    }

    /**
     * Return next n-byte data from bytes.
     */
    public final byte[] getData(final int length)
    {
        byte[] bytes = new byte[length];
        fillBytes(bytes, 0, length);
        return bytes;
    }

    /**
     * Return all remaining data from bytes.
     */
    public final byte[] getData()
    {
        return getData(0, limit);
    }


}
